//! HMAC签名算法实现
//! 
//! 提供HMAC-SHA256等签名功能

use hmac::{Hmac, Mac};
use sha2::Sha256;

/// 加密错误类型
#[derive(Debug, thiserror::Error)]
pub enum CryptoError {
    #[error("HMAC error: {0}")]
    Hmac(String),
}

pub type CryptoResult<T> = Result<T, CryptoError>;

/// HMAC-SHA256签名
/// 
/// # Examples
/// ```
/// use librarys::crypto::hash::hmac_sha256;
/// 
/// let signature = hmac_sha256("hello world", "secret_key").unwrap();
/// assert!(signature.len() == 64); // SHA256 = 32 bytes = 64 hex chars
/// ```
pub fn hmac_sha256(data: &str, key: &str) -> CryptoResult<String> {
    type HmacSha256 = Hmac<Sha256>;
    
    let mut mac = HmacSha256::new_from_slice(key.as_bytes())
        .map_err(|e| CryptoError::Hmac(e.to_string()))?;
    mac.update(data.as_bytes());
    let result = mac.finalize();
    Ok(format!("{:x}", result.into_bytes()))
}

/// HMAC-SHA256签名（字节输入）
/// 
/// # Examples
/// ```
/// use librarys::crypto::hash::hmac_sha256_bytes;
/// 
/// let signature = hmac_sha256_bytes(b"hello world", b"secret_key").unwrap();
/// assert!(signature.len() == 64);
/// ```
pub fn hmac_sha256_bytes(data: &[u8], key: &[u8]) -> CryptoResult<String> {
    type HmacSha256 = Hmac<Sha256>;
    
    let mut mac = HmacSha256::new_from_slice(key)
        .map_err(|e| CryptoError::Hmac(e.to_string()))?;
    mac.update(data);
    let result = mac.finalize();
    Ok(format!("{:x}", result.into_bytes()))
}

#[cfg(test)]
mod tests {
    use super::*;

    #[test]
    fn test_hmac() {
        let signature = hmac_sha256("hello world", "secret_key").unwrap();
        assert_eq!(signature.len(), 64); // SHA256 = 32 bytes = 64 hex chars
        
        // 相同输入应产生相同签名
        let signature2 = hmac_sha256("hello world", "secret_key").unwrap();
        assert_eq!(signature, signature2);
        
        // 不同密钥应产生不同签名
        let signature3 = hmac_sha256("hello world", "different_key").unwrap();
        assert_ne!(signature, signature3);
        
        // 测试字节输入
        let signature_bytes = hmac_sha256_bytes(b"hello world", b"secret_key").unwrap();
        assert_eq!(signature, signature_bytes);
    }
}